import { _decorator, BufferAsset, Component, Node } from 'cc';
import Socket from '../../../Core/Net/Socket';
import UrlEnum from '../../../Core/Res/UrlEnum';
import Instance from '../../../Core/Utils/Instance';
import ProtoBufManager from '../../proto/ProtoBufManager';
import { ProtocolHandler } from './AbstractModel';
import TimerMgr from '../../../Core/Timer/TimerMgr';
import { common, connector, http } from '../../proto/protoCmd.js';
import { ViewManager } from '../base/ViewManager';
import ModuleWindow from '../window/ModuleWindow';
import WindowObject from '../window/WindowObject';
import polemo from '../../polemo/polemo';
import { GameModels } from './GameModels';
import SdkTool from '../../../Core/Sdk/SdkTool';
import Log from '../../../Core/Log/Log';
import EventMgr from '../../../Core/Event/EventMgr';

export class SocketManager {
    private ws: Socket = null;
    private _callbackDict: Object;
    private _routeMap: Object;
    private heartbeatInterval: number;
    private heartbeatTimeout: number;
    private isReconnecting: any;
    private pingsign: number = Date.now();;
    public static get ins(): SocketManager {
        return Instance.get(SocketManager);
    }
    public init(): void {
        this._callbackDict = {};
        this._routeMap = {};
        if (!this.ws) {
            this.ws = new Socket();
        }
        this.ws.onMessage = this.onMessage.bind(this);
        this.ws.onOpen = () => {
            Log.ins.log(1, "Socket链接成功,发送handshakeBuffer");
            ViewManager.ins.removeLoading();
            this.sendHandShake();
        };
        this.ws.onClose = this.socketClose.bind(this);
        this.ws.onError = this.socketError.bind(this);
    }
    /**
     * 连接 WebSocket 服务器
     * @returns 无返回值
     */
    public connect() {
        ViewManager.ins.showloading();
        this.ws.connect(`${UrlEnum.socketip}`);
    }
    private sendHandShake(): void {
        const handshakeBuffer = {
            'sys': {
                type: "pitaya_cocos",
                version: "1.0.0",
                rsa: {}
            },
            'user': {}
        };

        const msg = polemo.Protocol.StrEncode(JSON.stringify(handshakeBuffer))
        const pkg = polemo.Package.Encode(polemo.Package.TYPE_HANDSHAKE, msg);
        this.ws.send(pkg.slice().buffer);
    }
    private onMessage(binary: Uint8Array) {
        const msgs = polemo.Package.Decode(binary);
        if (Array.isArray(msgs)) {
            for (let i = 0; i < msgs.length; i++) {
                let msg = msgs[i];
                this.dispatch(msg)
            }
        } else {
            this.dispatch(msgs)
        }
        return
    }
    private dispatch(msg): void {
        switch (msg.type) {
            case polemo.Package.TYPE_HANDSHAKE:
                {
                    const data = JSON.parse(polemo.Protocol.StrDecode(msg.body));
                    if (data.code === 501) {
                        return;
                    }
                    if (data.code !== 200) {
                        return;
                    }
                    if (data.sys && data.sys.heartbeat) {
                        this.heartbeatInterval = data.sys.heartbeat * 1000; // heartbeat interval
                        this.heartbeatTimeout = this.heartbeatInterval * 1.5; // max heartbeat timeout
                    } else {
                        this.heartbeatInterval = 0;
                        this.heartbeatTimeout = 0;
                    }
                    //step6: handshake回应，不然服务器会断开
                    const obj = polemo.Package.Encode(polemo.Package.TYPE_HANDSHAKE_ACK);
                    Log.ins.log(1, `收到handshake,发送回应`, `发送进入游戏大厅消息`, `开启心跳循环，间隔30秒`)
                    this.ws.send(obj.slice().buffer);
                    GameModels.login.send_ReqEnterLobby();
                    TimerMgr.ins.add(30000, -1, this.send_heartPing, this);
                    GameModels.regRouteMap();
                }
                break
            case polemo.Package.TYPE_HEARTBEAT:
                // 已弃用
                break
            case polemo.Package.TYPE_DATA:
                this.onData(msg.body);
                break
            case polemo.Package.TYPE_KICK:
                Log.ins.log(1, `被服务器踢下线`);
                // this.socketClose();
                //被踢掉
                // this.onKick(msg.body)
                break
            default:
                Log.ins.log(3, msg);
                break;
        }
    }
    /**接受数据 */
    public onData(buf): void {
        const msg = polemo.Message.Decode(buf);
        switch (msg.type) {
            case polemo.Message.TYPE_RESPONSE:
                {
                    if (msg.id == 99999) {
                        const info: connector.RespHeartBeat = ProtoBufManager.ins.connector.RespHeartBeat.decode(msg.body);
                        EventMgr.emit(`event_RespHeartBeat`, info.c);
                        //心跳，不用处理
                        return;
                    }
                    const handlers: Array<ProtocolHandler> = this._callbackDict[msg.id];
                    if (handlers) {
                        for (let i = 0; i < handlers.length; ++i) {
                            handlers[i].apply(msg.body);
                        }
                    } else {
                        Log.ins.log(2, `客户端未注册该协议回调:${msg.id}`);
                    }
                }
                break;
            case polemo.Message.TYPE_PUSH:
                {
                    const handlers: Array<ProtocolHandler> = this._routeMap[msg.route];
                    if (handlers) {
                        for (let i = 0; i < handlers.length; ++i) {
                            handlers[i].apply(msg.body);
                        }
                    } else {
                        Log.ins.log(2, `客户端未注册该协议回调:${msg.id}`);
                    }
                }
                break;
        }
    }
    // 发送
    public sendRequest(msgId: number, route, req: any) {
        const msg = polemo.Message.Encode(msgId, polemo.Message.TYPE_REQUEST, 0, route, req);
        const packet = polemo.Package.Encode(polemo.Package.TYPE_DATA, msg);
        this.ws.send(packet.slice().buffer);
    }
    private socketClose(): void {
        Log.ins.log(3, "Socket链接已关闭", `注销所有连接`, "移除心跳", "关闭所有窗口", "弹出重新连接界面");
        GameModels.game.removeRouteMap();
        GameModels.lobby.removeRouteMap();
        ModuleWindow.ins.closeAllWindows();
        TimerMgr.ins.remove(this.send_heartPing, this);
        ModuleWindow.ins.openWindow(WindowObject.ins.AlertWindow, `服务器断开连接，是否重连？`, () => {
            Log.ins.log(2, `玩家通过重连按钮拉起登录`);
            SdkTool.ins.sdk.login();
        });
        // this.connect();
        // ModuleWindow.ins.openWindow(WindowObject.ins.ReconnectWindow);
    }
    private socketError(): void {
        Log.ins.log(4, "Socket链接发生错误,移除心跳");
        TimerMgr.ins.remove(this.send_heartPing, this);
        // ModuleWindow.ins.openWindow(WindowObject.ins.ReconnectWindow);
    }
    /**
     * 发送心跳包
     * @returns 无返回值
     */
    private send_heartPing(): void {
        const sign = Date.now() - this.pingsign;
        if (sign < 1000) {
            this.pingsign = Date.now();
            return;
        }
        this.pingsign = Date.now();
        const heart = ProtoBufManager.ins.connector.ReqHeartBeat.create();
        const data = ProtoBufManager.ins.connector.ReqHeartBeat.encode(heart).finish();
        this.sendRequest(99999, "Connector.Connector.ReqHeartBeat", data)
    }
    /**
     *  注册协议处理器
     *
     * @param protocolID 协议ID
     * @param handler 协议处理器
     * @returns 无返回值
     */
    public registerHandler(protocolID: any, handler: ProtocolHandler): void {
        // var callbacks: Object = this._callbackDict[protocolID];
        if (this._callbackDict[protocolID] == null) {
            this._callbackDict[protocolID] = [];
        }
        const handlers: Array<ProtocolHandler> = this._callbackDict[protocolID];
        for (var i: number = 0; i < handlers.length; ++i) {
            if (handlers[i].protocolID == handler.protocolID) {
                return;
            }
        }
        handlers.push(handler);
    }
    public addRouteMap(protocolID: any, handler: ProtocolHandler): void {
        if (this._routeMap[protocolID] == null) {
            this._routeMap[protocolID] = [];
        }
        const handlers: Array<ProtocolHandler> = this._routeMap[protocolID];
        for (var i: number = 0; i < handlers.length; ++i) {
            if (handlers[i].protocolID == handler.protocolID) {
                return;
            }
        }
        handlers.push(handler);
    }
    /**
  * 注销协议回调
  */
    public unregisterHandler(protocolID: number, handler: ProtocolHandler): void {
        const handlers: Array<ProtocolHandler> = this._callbackDict[protocolID];
        for (var i: number = 0; i < handlers.length; ++i) {
            if (handlers[i] == handler) {
                handlers.splice(i, 1);
                return;
            }
        }
    }
    /**
     * 注销路由回调
     * @param protocolID 
     * @param handler 
     * @returns 
     */
    public unRouteHandler(protocolID: any): void {
        const handlers: Array<ProtocolHandler> = this._routeMap[protocolID];
        if (handlers) {
            handlers.length = 0;
        }
    }
    public get wsIsClose(): boolean {
        return this.ws && this.ws.isClose();
    }
}


